package cn.iocoder.yudao.module.system.tcpserver.protocol1;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.HexUtil;
import cn.iocoder.yudao.module.system.dal.dataobject.zkdata.ZkDataDO;
import cn.iocoder.yudao.module.system.dal.mysql.zkdata.ZkDataMapper;
import cn.iocoder.yudao.module.system.tcpserver.pojo.HeartInfo;
import cn.iocoder.yudao.module.system.tcpserver.pojo.ZkPacketFrame;
import cn.iocoder.yudao.module.system.tcpserver.utils.ZkCrcUtils;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class ZkDataProcessService {

    public static final Map<String, HeartInfo> clientMap = new ConcurrentHashMap<>();

    @Resource
    private ZkDataMapper zkDataMapper;

    public void sendData(String deviceNo, Channel channel) {
        List<ZkDataDO> dataToSend = getDataToSend(deviceNo);
        if (CollUtil.isNotEmpty(dataToSend)) {
            dataToSend.forEach(data -> {
                channel.writeAndFlush(new ZkPacketFrame(HexUtil.decodeHex(data.getOriData())));
                updateStatus(data);
            });
        }
    }

    public List<ZkDataDO> getDataToSend(String receiver) {
        List<ZkDataDO> zkDataDOS = zkDataMapper.selectList(ZkDataDO::getDataReceiver, receiver, ZkDataDO::getStatus, 0);
        return zkDataDOS;
    }

    public void updateStatus(ZkDataDO zkDataDO) {
        zkDataDO.setStatus(1);
        zkDataMapper.updateById(zkDataDO);
    }

    public ZkPacketFrame process(ZkDataDO msg) {

        if (msg.getDataType() == 0xA5) {
            //心跳数据
            byte[] bytes = heartProcess(msg);
            if (clientMap.containsKey(msg.getDataSender())) {
                //已上报过心跳
                LocalDateTime now = LocalDateTime.now();
                HeartInfo heartInfo = clientMap.get(msg.getDataSender());
                heartInfo.setData(bytes);
                heartInfo.setDateTime(now);
                if (heartInfo.getFirstTime() == null) {
                    log.info("已上报过心跳，但中间断开了:{}", msg.getDataSender());
                    //已上报过心跳，但中间断开了
                    heartInfo.setFirstTime(now);
                    return new ZkPacketFrame(bytes);
                } else {
                    //已上报过心跳，且未断开过
                    return new ZkPacketFrame(null);
                }

            } else {
                log.info("第一次心跳:{}", msg.getDataSender());
                //第一次上报心跳
                LocalDateTime now = LocalDateTime.now();
                HeartInfo build = HeartInfo.builder().data(bytes)
                        .dateTime(now).firstTime(now).sender(msg.getDataSender()).build();
                clientMap.put(msg.getDataSender(), build);
                return new ZkPacketFrame(bytes);
            }

        } else {
            //业务数据
            zkDataMapper.insert(msg);

            if (msg.getStatus() == 2) {
                log.warn("crc error - sender:{}, data: {}", msg.getDataSender(), HexUtil.decodeHex(msg.getOriData()));
            }
//            else {
//                if (HeartbeatServer.getClientMap().containsKey(msg.getDataReceiver())) {
//                    HeartbeatServer.ChannelSchedule channelSchedule = HeartbeatServer.getClientMap().get(msg.getDataReceiver());
//                    sendData(msg.getDataReceiver(), channelSchedule.channel);
//                }
//            }
            return new ZkPacketFrame(null);
        }
    }


    public void sendServerHeartbeat(Channel channel, String sender) {
        HeartInfo heartInfo = clientMap.get(sender);
        if (LocalDateTimeUtil.between(heartInfo.getDateTime(), LocalDateTime.now(), ChronoUnit.SECONDS) > 60) {
            clientOffLine(sender);
        }

        if (LocalDateTimeUtil.between(heartInfo.getDateTime(), LocalDateTime.now(), ChronoUnit.SECONDS) > 300) {
            log.info("超时断开:{}", sender);
            cleanupClient(sender);
        } else {
            if (channel.isActive()) {
                if (clientMap.containsKey(sender)) {
                    // 根据clientId生成自定义内容（示例逻辑）
                    log.info("sender:{}-heartbeat", sender);
                    channel.writeAndFlush(new ZkPacketFrame(heartInfo.getData()));
                } else {
                    log.warn("sender:{} not exist", sender);
                }

            } else {
                // 清理无效连接
                cleanupClient(sender);
            }
        }

    }

    public void clientOffLine(String sender) {
        if (clientMap.containsKey(sender)) {
            clientMap.get(sender).setFirstTime(null);
        }
    }

    private void cleanupClient(String clientId) {
        clientOffLine(clientId);
        HeartbeatServer.ChannelSchedule schedule = HeartbeatServer.getClientMap().remove(clientId);
        if (schedule != null && schedule.future != null) {
            schedule.future.cancel(true);
            log.info("心跳任务关闭：{}", clientId);
        }
        if (schedule != null && schedule.channel != null) {
            schedule.channel.close();
            log.info("连接关闭：{}", clientId);
        }
    }

    public byte[] heartProcess(ZkDataDO msg) {

        String oriData = msg.getOriData();
        byte[] bytes = HexUtil.decodeHex(oriData);
        int length = bytes.length;
        byte[] oriSender = HexUtil.decodeHex(msg.getDataSender());
        bytes[4] = oriSender[0];
        bytes[5] = oriSender[1];
        bytes[6] = (byte) 0xFF;
        bytes[7] = (byte) 0xFF;
        byte[] crc = ZkCrcUtils.getCrc(bytes);
        bytes[length - 2] = crc[0];
        bytes[length - 1] = crc[1];
        return bytes;
    }


}
